package gameEngine

import (
	"MetroBuilderGameJam/gameEngine/drawer"
	"MetroBuilderGameJam/gameEngine/geom"
	"MetroBuilderGameJam/gameEngine/render"
	"MetroBuilderGameJam/gameEngine/utils"
	"MetroBuilderGameJam/guiElement"
	"MetroBuilderGameJam/world"
	"fmt"
	"github.com/hajimehoshi/ebiten/v2"
	"github.com/hajimehoshi/ebiten/v2/inpututil"
)

type Clickable interface {
	Click()
}

type ItemsContainer interface {
	GetStoredTrains() []*world.Train
	StartDragTrain(train *world.Train, position geom.Point)
	GetLines() []*world.RailRoad
	GetScore() int
}

func NewGui(windowSize geom.IntSize, game *Game) *Gui {
	d := drawer.NewCentralDrawer()
	return &Gui{
		windowSize:     windowSize,
		buttonPainter:  render.NewButtonRenderer(d, windowSize),
		colliderRender: render.NewColliderRenderer(d),
		textPainter:    render.NewTextOnRectangleRenderer(d, windowSize),
		lineButtons:    make([]*guiElement.Button, 0, 8),
		CanContinue:    true,
		speedButtons: []*guiElement.Button{
			guiElement.NewButton(guiElement.ButtonPause, "", geom.Point{-20, 20}, geom.Size{10, 10}, func() bool {
				return !game.isPaused
			}, func() {
				game.isPaused = true
			}),
			guiElement.NewButton(guiElement.ButtonSpeed1, "", geom.Point{-20, 50}, geom.Size{10, 10}, func() bool {
				return game.isPaused || game.speed != utils.SpeedNormal
			}, func() {
				game.isPaused = false
				game.speed = utils.SpeedNormal
			}),
			guiElement.NewButton(guiElement.ButtonSpeed2, "", geom.Point{-20, 80}, geom.Size{10, 10}, func() bool {
				return game.isPaused || game.speed != utils.SpeedFast
			}, func() {
				game.isPaused = false
				game.speed = utils.SpeedFast
			}),
		},
	}
}

type Gui struct {
	windowSize     geom.IntSize
	buttonPainter  *render.ButtonRenderer
	textPainter    *render.TextRenderer
	colliderRender *render.Collider

	speedButtons []*guiElement.Button
	trainButton  *guiElement.Button

	lineButtons       []*guiElement.Button
	visibleElements   []utils.ItemFigure
	score             int
	IsMainMenuVisible bool
	CanContinue       bool
	OnRestartLevel    func()
	OnContinue        func()
	notification      string
}

func (g *Gui) Draw(screen *ebiten.Image) []utils.ItemFigure {
	figures := []utils.ItemFigure{}

	g.textPainter.Draw(screen, fmt.Sprintf("Score: %d", g.score), geom.Point{X: -150, Y: 20})

	if g.IsMainMenuVisible {
		g.DrawOverlay(screen)
		g.textPainter.Draw(screen, "Metro Golden Builder", geom.Point{X: 350, Y: 100})
		if g.notification != "" {
			g.textPainter.Draw(screen, g.notification, geom.Point{X: 100, Y: 130})
		} else {
			if !g.CanContinue {
				g.textPainter.Draw(screen, "Game over! Try again.", geom.Point{X: 100, Y: 130})
			}
			g.textPainter.Draw(screen, "Your goal is to build the wolrd's best subway system.", geom.Point{X: 100, Y: 150})
			g.textPainter.Draw(screen, "Try to drag a line between two stations to connect them.", geom.Point{X: 100, Y: 170})
			g.textPainter.Draw(screen, "Also you can drag and drop trains on railroad.", geom.Point{X: 100, Y: 280})
			g.textPainter.Draw(screen, "Also you can change and rebuild any line, feel free.", geom.Point{X: 100, Y: 300})
		}
		for _, b := range g.getMainMenuButtons() {
			figure := g.buttonPainter.Draw(screen, b)
			if figure != nil {
				//g.colliderRender.Draw(screen, figure.GetCollider())
				figures = append(figures, figure)
			}
		}
	} else {
		for _, b := range g.getButtons() {
			figure := g.buttonPainter.Draw(screen, b)
			if figure != nil {
				//g.colliderRender.Draw(screen, figure.GetCollider())
				figures = append(figures, figure)
			}
		}
	}

	g.visibleElements = figures

	return figures
}

func (g *Gui) UserControl(hover, _ utils.ItemFigure) {
	if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) {
		if hover != nil && hover.GetItem() != nil {
			if cl, ok := hover.GetItem().(Clickable); ok {
				g.ClickOn(cl)
			}
		}
	}
}

func (g *Gui) ClickOn(item Clickable) {
	if item == nil {
		return
	}

	item.Click()
}

func (g *Gui) InteractiveCursor(x int, y int) utils.ItemFigure {
	for i := len(g.visibleElements) - 1; i >= 0; i-- {
		if g.visibleElements[i].GetCollider().Contains(geom.IntPoint{X: x, Y: y}.AsPoint()) {
			return g.visibleElements[i]
		}
	}
	return nil
}

func (g *Gui) UpdateGameItems(container ItemsContainer) {
	trains := container.GetStoredTrains()
	p := geom.Point{X: 20, Y: 50}
	g.trainButton = guiElement.NewButton(guiElement.ButtonTrain, fmt.Sprintf("%d", len(trains)), p, geom.Size{Width: 8, Height: 6}, func() bool {
		return len(trains) > 0
	}, func() {
		if len(trains) == 0 {
			return
		}
		container.StartDragTrain(trains[0], p)
	})

	g.score = container.GetScore()
	if len(g.lineButtons) != len(container.GetLines()) {
		g.lineButtons = make([]*guiElement.Button, len(container.GetLines()))
		distance := float64(30)
		basePosition := geom.NewVec2(float64(g.windowSize.Width)/2-float64(len(g.lineButtons))*distance/2, -30)
		for i, line := range container.GetLines() {
			p := basePosition.Add(geom.NewVec2(float64(i+1)*distance, 0)).AsPoint()
			g.lineButtons[i] = guiElement.NewButton(guiElement.ButtonLine, fmt.Sprintf("%d", i), p, geom.Size{Width: 4, Height: 4}, func() bool {
				return line.IsAvailable()
			}, func() {
				return
			})
			g.lineButtons[i].SetColor(line.GetColor())
		}
	}
	for i, line := range container.GetLines() {
		if line.Count() > 0 {
			g.lineButtons[i].SetSize(geom.Size{Width: 10, Height: 10})
		}
	}
	g.trainButton.SetText(fmt.Sprintf("%d", len(trains)))
}

func (g *Gui) getButtons() []*guiElement.Button {
	buttons := make([]*guiElement.Button, 0, len(g.speedButtons)+1+len(g.lineButtons))
	buttons = append(buttons, g.speedButtons...)
	if g.trainButton != nil {
		buttons = append(buttons, g.trainButton)
	}
	buttons = append(buttons, g.lineButtons...)
	return buttons
}

func (g *Gui) getMainMenuButtons() []*guiElement.Button {
	buttons := make([]*guiElement.Button, 0, 2)
	if g.notification == "" {
		buttons = append(buttons, guiElement.NewButton(guiElement.MenuButton, "Start New Game", geom.Point{X: 250, Y: 200}, geom.Size{Width: 100, Height: 25}, func() bool {
			return true
		}, g.OnRestartLevel))
	}
	if g.CanContinue {
		buttons = append(buttons,
			guiElement.NewButton(guiElement.MenuButton, "Continue", geom.Point{X: 450, Y: 200}, geom.Size{Width: 80, Height: 25}, func() bool {
				return true
			}, g.OnContinue))
	}
	return buttons
}

func (g *Gui) DrawOverlay(screen *ebiten.Image) {
	drawer.NewCentralDrawer().FillSimpleRectangle(screen, geom.NewVec2(0, 0).AsPoint(), g.windowSize.AsVec2().ToSize(), drawer.ColorTransparent(drawer.ColorGray, 0.5))
}

func (g *Gui) CloseMainMenu() {
	g.IsMainMenuVisible = false
	g.notification = ""
}
